from pyjvm.instructions.common import (
    BytecodeReader, ClassInitializer, Instruction, NOP
)
from pyjvm.runtime.thread import Frame

from pyjvm.instructions.store import (
    ISTORE, LSTORE, FSTORE, DSTORE, ASTORE, ISTORE_0, ISTORE_1,
    ISTORE_2, ISTORE_3, LSTORE_0, LSTORE_1, LSTORE_2, LSTORE_3,
    FSTORE_0, FSTORE_1, FSTORE_2, FSTORE_3, DSTORE_0, DSTORE_1,
    DSTORE_2, DSTORE_3, ASTORE_0, ASTORE_1, ASTORE_2, ASTORE_3,
    IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE
)

from pyjvm.instructions.loads import (
    ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ILOAD_0, ILOAD_1, ILOAD_2,
    ILOAD_3, LLOAD_0, LLOAD_1, LLOAD_2, LLOAD_3, FLOAD_0, FLOAD_1,
    FLOAD_2, FLOAD_3, DLOAD_0, DLOAD_1, DLOAD_2, DLOAD_3, ALOAD_0,
    ALOAD_1, ALOAD_2, ALOAD_3, IALOAD, LALOAD, FALOAD, DALOAD,
    AALOAD, BALOAD, CALOAD, SALOAD,
)

from pyjvm.instructions.consts import (
    ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3,
    ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1,
    FCONST_2, DCONST_0, DCONST_1, BIPUSH, SIPUSH, LDC, LDC_W, LDC2_W
)

from pyjvm.instructions.stack import (
    POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
)

from pyjvm.instructions.math import (
    IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL,
    FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM,
    INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR,
    IAND, LAND, IOR, LOR, IXOR, LXOR, IINC,
)

from pyjvm.instructions.conversions import (
    I2L, I2F, I2D, L2I, L2F, L2D, F2I,
    F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S,
)

from pyjvm.instructions.cmps import (
    LCMP, FCMPL, FCMPG, DCMPL, DCMPG
)

from pyjvm.instructions.conditions import (
    IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT,
    IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, IFNULL, IFNONNULL
)

from pyjvm.instructions.control import (
    GOTO, JSR, RET, TABLE_SWITCH, LOOKUP_SWITCH, IRETURN, LRETURN,
    FRETURN, DRETURN, ARETURN, RETURN, GOTO_W
)

from pyjvm.instructions.refs import (
    GET_STATIC, PUT_STATIC, GET_FIELD, PUT_FIELD, INVOKE_VIRTUAL,
    INVOKE_SPECIAL, INVOKE_STATIC, INVOKE_INTERFACE, INVOKE_DYNAMIC, NEW,
    NEW_ARRAY, ANEW_ARRAY, ARRAY_LENGTH, ATHROW, CHECK_CAST,
    INSTANCE_OF, MONITOR_ENTER, MONITOR_EXIT, MULTI_ANEW_ARRAY,
)

from pyjvm.instructions.native import INVOKE_NATIVE

OP_CODE_INSTRUCTION_MAP = {
    0x00:
        NOP(),
    0x01:
        ACONST_NULL(),
    0x02:
        ICONST_M1(),
    0x03:
        ICONST_0(),
    0x04:
        ICONST_1(),
    0x05:
        ICONST_2(),
    0x06:
        ICONST_3(),
    0x07:
        ICONST_4(),
    0x08:
        ICONST_5(),
    0x09:
        LCONST_0(),
    0x0a:
        LCONST_1(),
    0x0b:
        FCONST_0(),
    0x0c:
        FCONST_1(),
    0x0d:
        FCONST_2(),
    0x0e:
        DCONST_0(),
    0x0f:
        DCONST_1(),
    0x10:
        BIPUSH(),
    0x11:
        SIPUSH(),
    0x12:
        LDC(),
    0x13:
        LDC_W(),
    0x14:
        LDC2_W(),
    0x15:
        ILOAD(),
    0x16:
        LLOAD(),
    0x17:
        FLOAD(),
    0x18:
        DLOAD(),
    0x19:
        ALOAD(),
    0x1a:
        ILOAD_0(),
    0x1b:
        ILOAD_1(),
    0x1c:
        ILOAD_2(),
    0x1d:
        ILOAD_3(),
    0x1e:
        LLOAD_0(),
    0x1f:
        LLOAD_1(),
    0x20:
        LLOAD_2(),
    0x21:
        LLOAD_3(),
    0x22:
        FLOAD_0(),
    0x23:
        FLOAD_1(),
    0x24:
        FLOAD_2(),
    0x25:
        FLOAD_3(),
    0x26:
        DLOAD_0(),
    0x27:
        DLOAD_1(),
    0x28:
        DLOAD_2(),
    0x29:
        DLOAD_3(),
    0x2a:
        ALOAD_0(),
    0x2b:
        ALOAD_1(),
    0x2c:
        ALOAD_2(),
    0x2d:
        ALOAD_3(),
    0x2e:
        IALOAD(),
    0x2f:
        LALOAD(),
    0x30:
        FALOAD(),
    0x31:
        DALOAD(),
    0x32:
        AALOAD(),
    0x33:
        BALOAD(),
    0x34:
        CALOAD(),
    0x35:
        SALOAD(),
    0x36:
        ISTORE(),
    0x37:
        LSTORE(),
    0x38:
        FSTORE(),
    0x39:
        DSTORE(),
    0x3a:
        ASTORE(),
    0x3b:
        ISTORE_0(),
    0x3c:
        ISTORE_1(),
    0x3d:
        ISTORE_2(),
    0x3e:
        ISTORE_3(),
    0x3f:
        LSTORE_0(),
    0x40:
        LSTORE_1(),
    0x41:
        LSTORE_2(),
    0x42:
        LSTORE_3(),
    0x43:
        FSTORE_0(),
    0x44:
        FSTORE_1(),
    0x45:
        FSTORE_2(),
    0x46:
        FSTORE_3(),
    0x47:
        DSTORE_0(),
    0x48:
        DSTORE_1(),
    0x49:
        DSTORE_2(),
    0x4a:
        DSTORE_3(),
    0x4b:
        ASTORE_0(),
    0x4c:
        ASTORE_1(),
    0x4d:
        ASTORE_2(),
    0x4e:
        ASTORE_3(),
    0x4f:
        IASTORE(),
    0x50:
        LASTORE(),
    0x51:
        FASTORE(),
    0x52:
        DASTORE(),
    0x53:
        AASTORE(),
    0x54:
        BASTORE(),
    0x55:
        CASTORE(),
    0x56:
        SASTORE(),
    0x57:
        POP(),
    0x58:
        POP2(),
    0x59:
        DUP(),
    0x5a:
        DUP_X1(),
    0x5b:
        DUP_X2(),
    0x5c:
        DUP2(),
    0x5d:
        DUP2_X1(),
    0x5e:
        DUP2_X2(),
    0x5f:
        SWAP(),
    0x60:
        IADD(),
    0x61:
        LADD(),
    0x62:
        FADD(),
    0x63:
        DADD(),
    0x64:
        ISUB(),
    0x65:
        LSUB(),
    0x66:
        FSUB(),
    0x67:
        DSUB(),
    0x68:
        IMUL(),
    0x69:
        LMUL(),
    0x6a:
        FMUL(),
    0x6b:
        DMUL(),
    0x6c:
        IDIV(),
    0x6d:
        LDIV(),
    0x6e:
        FDIV(),
    0x6f:
        DDIV(),
    0x70:
        IREM(),
    0x71:
        LREM(),
    0x72:
        FREM(),
    0x73:
        DREM(),
    0x74:
        INEG(),
    0x75:
        LNEG(),
    0x76:
        FNEG(),
    0x77:
        DNEG(),
    0x78:
        ISHL(),
    0x79:
        LSHL(),
    0x7a:
        ISHR(),
    0x7b:
        LSHR(),
    0x7c:
        IUSHR(),
    0x7d:
        LUSHR(),
    0x7e:
        IAND(),
    0x7f:
        LAND(),
    0x80:
        IOR(),
    0x81:
        LOR(),
    0x82:
        IXOR(),
    0x83:
        LXOR(),
    0x84:
        IINC(),
    0x85:
        I2L(),
    0x86:
        I2F(),
    0x87:
        I2D(),
    0x88:
        L2I(),
    0x89:
        L2F(),
    0x8a:
        L2D(),
    0x8b:
        F2I(),
    0x8c:
        F2L(),
    0x8d:
        F2D(),
    0x8e:
        D2I(),
    0x8f:
        D2L(),
    0x90:
        D2F(),
    0x91:
        I2B(),
    0x92:
        I2C(),
    0x93:
        I2S(),
    0x94:
        LCMP(),
    0x95:
        FCMPL(),
    0x96:
        FCMPG(),
    0x97:
        DCMPL(),
    0x98:
        DCMPG(),
    0x99:
        IFEQ(),
    0x9a:
        IFNE(),
    0x9b:
        IFLT(),
    0x9c:
        IFGE(),
    0x9d:
        IFGT(),
    0x9e:
        IFLE(),
    0x9f:
        IF_ICMPEQ(),
    0xa0:
        IF_ICMPNE(),
    0xa1:
        IF_ICMPLT(),
    0xa2:
        IF_ICMPGE(),
    0xa3:
        IF_ICMPGT(),
    0xa4:
        IF_ICMPLE(),
    0xa5:
        IF_ACMPEQ(),
    0xa6:
        IF_ACMPNE(),
    0xa7:
        GOTO(),
    # 0xa8:
    #     JSR(),
    # 0xa9:
    #     RET(),
    0xaa:
        TABLE_SWITCH(),
    0xab:
        LOOKUP_SWITCH(),
    0xac:
        IRETURN(),
    0xad:
        LRETURN(),
    0xae:
        FRETURN(),
    0xaf:
        DRETURN(),
    0xb0:
        ARETURN(),
    0xb1:
        RETURN(),
    0xb2:
        GET_STATIC(),
    0xb3:
        PUT_STATIC(),
    0xb4:
        GET_FIELD(),
    0xb5:
        PUT_FIELD(),
    0xb6:
        INVOKE_VIRTUAL(),
    0xb7:
        INVOKE_SPECIAL(),
    0xb8:
        INVOKE_STATIC(),
    0xb9:
        INVOKE_INTERFACE(),
    #  0xba:
    #      INVOKE_DYNAMIC(),
    0xbb:
        NEW(),
    0xbc:
        NEW_ARRAY(),
    0xbd:
        ANEW_ARRAY(),
    0xbe:
        ARRAY_LENGTH(),
    0xbf:
        ATHROW(),
    0xc0:
        CHECK_CAST(),
    0xc1:
        INSTANCE_OF(),
    0xc2:
        MONITOR_ENTER(),
    0xc3:
        MONITOR_EXIT(),
    0xc5:
        MULTI_ANEW_ARRAY(),
    0xc6:
        IFNULL(),
    0xc7:
        IFNONNULL(),
    0xc8:
        GOTO_W(),
    #  0xc9:
    #      JSR_W(),
    #  0xca: breakpoint
    0xfe:
        INVOKE_NATIVE(),
}


def get_instruction(op_code: int) -> Instruction:
    return OP_CODE_INSTRUCTION_MAP.get(op_code, None)


class WIDE(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.instruction = None

    def fetch_operands(self, reader: BytecodeReader):
        op_code = reader.uint8()
        self.instruction = get_instruction(op_code)
        self.instruction.index = reader.uint16()
        if isinstance(self.instruction, IINC):
            self.instruction.const_value = reader.int16()

    def execute(self, frame: Frame):
        self.instruction.execute(frame)


OP_CODE_INSTRUCTION_MAP[0xc4] = WIDE()

__all__ = ['BytecodeReader', 'ClassInitializer',
           'NOP', 'ACONST_NULL', 'ICONST_M1', 'ICONST_0', 'ICONST_1',
           'ICONST_2', 'ICONST_3', 'ICONST_4', 'ICONST_5', 'LCONST_0',
           'LCONST_1', 'FCONST_0', 'FCONST_1', 'FCONST_2', 'DCONST_0',
           'DCONST_1', 'BIPUSH', 'SIPUSH', 'LDC', 'LDC_W', 'LDC2_W',
           'ILOAD', 'LLOAD', 'FLOAD', 'DLOAD', 'ALOAD', 'ILOAD_0',
           'ILOAD_1', 'ILOAD_2', 'ILOAD_3', 'LLOAD_0', 'LLOAD_1',
           'LLOAD_2', 'LLOAD_3', 'FLOAD_0', 'FLOAD_1', 'FLOAD_2',
           'FLOAD_3', 'DLOAD_0', 'DLOAD_1', 'DLOAD_2', 'DLOAD_3',
           'ALOAD_0', 'ALOAD_1', 'ALOAD_2', 'ALOAD_3', 'IALOAD',
           'LALOAD', 'FALOAD', 'DALOAD', 'AALOAD', 'BALOAD', 'CALOAD',
           'SALOAD', 'ISTORE', 'LSTORE', 'FSTORE', 'DSTORE', 'ASTORE',
           'ISTORE_0', 'ISTORE_1', 'ISTORE_2', 'ISTORE_3', 'LSTORE_0',
           'LSTORE_1', 'LSTORE_2', 'LSTORE_3', 'FSTORE_0', 'FSTORE_1',
           'FSTORE_2', 'FSTORE_3', 'DSTORE_0', 'DSTORE_1', 'DSTORE_2',
           'DSTORE_3', 'ASTORE_0', 'ASTORE_1', 'ASTORE_2', 'ASTORE_3',
           'IASTORE', 'LASTORE', 'FASTORE', 'DASTORE', 'AASTORE',
           'BASTORE', 'CASTORE', 'SASTORE', 'POP', 'POP2', 'DUP',
           'DUP_X1', 'DUP_X2', 'DUP2', 'DUP2_X1', 'DUP2_X2', 'SWAP',
           'IADD', 'LADD', 'FADD', 'DADD', 'ISUB', 'LSUB', 'FSUB',
           'DSUB', 'IMUL', 'LMUL', 'FMUL', 'DMUL', 'IDIV', 'LDIV',
           'FDIV', 'DDIV', 'IREM', 'LREM', 'FREM', 'DREM', 'INEG',
           'LNEG', 'FNEG', 'DNEG', 'ISHL', 'LSHL', 'ISHR', 'LSHR',
           'IUSHR', 'LUSHR', 'IAND', 'LAND', 'IOR', 'LOR', 'IXOR',
           'LXOR', 'IINC', 'I2L', 'I2F', 'I2D', 'L2I', 'L2F', 'L2D',
           'F2I', 'F2L', 'F2D', 'D2I', 'D2L', 'D2F', 'I2B', 'I2C',
           'I2S', 'LCMP', 'FCMPL', 'FCMPG', 'DCMPL', 'DCMPG', 'IFEQ',
           'IFNE', 'IFLT', 'IFGE', 'IFGT', 'IFLE', 'IF_ICMPEQ',
           'IF_ICMPNE', 'IF_ICMPLT', 'IF_ICMPGE', 'IF_ICMPGT',
           'IF_ICMPLE', 'IF_ACMPEQ', 'IF_ACMPNE', 'GOTO', 'JSR',
           'RET', 'TABLE_SWITCH', 'LOOKUP_SWITCH', 'IRETURN',
           'LRETURN', 'FRETURN', 'DRETURN', 'ARETURN', 'RETURN',
           'GET_STATIC', 'PUT_STATIC', 'GET_FIELD', 'PUT_FIELD',
           'INVOKE_VIRTUAL', 'INVOKE_SPECIAL', 'INVOKE_STATIC',
           'INVOKE_INTERFACE', 'INVOKE_DYNAMIC', 'NEW', 'NEW_ARRAY',
           'ANEW_ARRAY', 'ARRAY_LENGTH', 'ATHROW', 'CHECK_CAST',
           'INSTANCE_OF', 'MONITOR_ENTER', 'MONITOR_EXIT', 'WIDE',
           'MULTI_ANEW_ARRAY', 'IFNULL', 'IFNONNULL', 'GOTO_W',
           'INVOKE_NATIVE', 'get_instruction'
           ]
